package biolabs.space;

import java.util.Arrays;

public class GenericGrid<T, P extends DiscretePosition<P,C>, C extends ContinuousPosition<C,P>>
		extends Grid<T, P, C> {

	private final int dim;
	protected final P size;
	protected final T[] array;

	@SuppressWarnings("unchecked")
	public GenericGrid(P size){
		this.dim = size.getDimension();
		this.size = size;
		int volume = 1;
		for(int i=0; i<dim; ++i){
			int c = size.getCoordinate(i);
			if(c <= 0)
				throw new IllegalArgumentException(size + " has a non-positive coordinate");
			volume *= c;
		}
		this.array = (T[])new Object[volume];
	}

	GenericGrid(Grid<T, P, C> grid, P origin, P size){
		this(size);
		if(!inRange(origin) || !inRange(origin.plus(size).minus(size.one())))
			throw new IndexOutOfBoundsException("The requested frame is out of range");
		grid._copyTo(this, size.zero(), origin, size);
	}

	@Override
	public P getSize(){
		return size;
	}

	@Override
	public boolean inRange(P pos){
		for(int i=0; i<dim; ++i){
			int x = pos.getCoordinate(i);
			if(x<0 || x>=size.getCoordinate(i))
				return false;
		}
		return true;
	}

	@Override
	public T at(P pos){
		if(!inRange(pos))
			throw new ArrayIndexOutOfBoundsException(pos + " is out of " + size);
		return array[computeIndex(pos)];
	}

	@Override
	protected T _setField(P pos, T value){
		int i = computeIndex(pos);
		T res = array[i];
		array[i] = value;
		return res;
	}

	private int	computeIndex(P p){
		int unit = 1;
		int index = 0;
		for(int i=dim-1; i>=0; --i){
			index += p.getCoordinate(i)*unit;
			unit *= size.getCoordinate(i);
		}
		return index;
	}

	@Override
	public Grid<T, P, C> copyOf(P origin, P size) {
		if(!inRange(origin) || !inRange(origin.plus(size).minus(size.one())))
			throw new IndexOutOfBoundsException("The requested frame is out of range");
		Grid<T, P, C> copy = new GenericGrid<T, P, C>(size);
		_copyTo(copy, origin.zero(), origin, size);
		return copy;
	}

	@Override
	public void fill(T value) {
		Arrays.fill(array, value);
	}
}
